---
title: 从HDFS中读取Avro数据
---

<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
-->


使用PXF HDFS连接器读取Avro格式数据。本节描述如何使用PXF访问HDFS中的Avro数据，包括如何创建和查询引用HDFS中Avro文件的外部表。

## <a id="prereq"></a>先决条件

在尝试从HDFS读取或向HDFS写入数据之前，请确保已满足PXF Hadoop[先决条件](access_hdfs.html#hadoop_prereq)。

## <a id="avro_work"></a>使用Avro数据

Apache Avro是一个数据序列化框架，其中的数据都以压缩二进制格式序列化。Avro在Json中指定数据类型。 Avro格式数据具有独立的模式, 这也在Json中定义。 Avro模式及其数据完全是自描述的。

### <a id="profile_hdfsavrodatamap"></a>数据类型映射

Avro支持原生数据类型和复杂数据类型。

要在Greenplum数据库中表示Avro原生数据类型，请将数据值映射到相同类型的Greenplum列。

Avro支持复杂数据类型数组(array), 映射(map), 记录(record), 枚举(enumeration)以及固定长度类型(fixed type)。 将这些复杂数据类型的顶层字段映射为Greenplum数据库的 `TEXT`类型。虽然Greenplum本身并不支持这些类型，您可以创建Greenplum数据库函数或应用程序代码来提取或进一步处理这些复杂数据类型的子部件。

下表总结了Avro数据的外部映射规则。

<a id="topic_oy3_qwm_ss__table_j4s_h1n_ss"></a>

| Avro数据类型| PXF/Greenplum 数据类型|
|-------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| boolean | boolean |
| bytes | bytea |
| double | double |
| float | real |
| int | int or smallint |
| long | bigint |
| string | text |
| 复杂类型: Array, Map, Record, or Enum                         | text, 并在集合项、映射的键值对和记录数据之间插入分隔符。|
| 复杂类型: Fixed                                               | bytea                                                                                                                                                                                               |
| 并集                                                             | 对于原始数据类型或复杂数据类型，遵循上述约定，具体取决于并集；支持Null值。|


### <a id="topic_tr3_dpg_ts__section_m2p_ztg_ts"></a>Avro 模式和数据

Avro 模式使用json定义，由上面数据类型映射部分中的原生类型和复杂类型组成。Avro 模式文件通常具有`.avsc`后缀。

Avro模式文件中的字段是通过对象数组定义的，每个对象都由名称和类型指定。


## <a id="profile_cet"></a>创建外部表

使用`hdfs:avro`配置文件读取HDFS中的Avro格式数据，以下语法创建了引用该配置文件的可读外部表：

``` sql
CREATE EXTERNAL TABLE <table_name>
    ( <column_name> <data_type> [, ...] | LIKE <other_table> )
LOCATION ('pxf://<path-to-hdfs-file>?PROFILE=hdfs:avro[&<custom-option>=<value>[...]]')
FORMAT 'CUSTOM' (FORMATTER='pxfwritable_import');
```

[CREATE EXTERNAL TABLE](../ref_guide/sql_commands/CREATE_EXTERNAL_TABLE.html)命令中使用的特定关键字和值见下表中描述。

| 关键字| 值|
|-------|-------------------------------------|
| \<path&#8209;to&#8209;hdfs&#8209;file\>    | HDFS数据存储中目录或文件的绝对路径 |
| PROFILE    | `PROFILE`关键字必须指定为`hdfs:avro`. |
| \<custom&#8209;option\>  | \<custom-option\>描述见下方|
| FORMAT 'CUSTOM' | 在 `hdfs:avro`配置文件中使用`FORMAT` `'CUSTOM'` 。`CUSTOM` `FORMAT` 要求您指定为 `(FORMATTER='pxfwritable_import')`。 |

<a id="customopts"></a>
对于复杂数据类型，PXF`hdfs:avro`配置文件在集合项和值之间插入默认分隔符。您可以通过指定在`CREATE EXTERNAL TABLE`命令中指定`hdfs:avro`自定义选项来使用非默认分隔符。

`hdfs:avro`配置文件支持以下\<custom-option\>：

| 选项关键字| 描述
|---------------|--------------------|                                                                                        
| COLLECTION_DELIM | 当PXF将Avro复杂数据类型映射到文本列时，放置在顶层数组、映射或记录字段中的条目之间的分隔符。默认为逗号`,`字符。 |
| MAPKEY_DELIM | 当PXF将Avro复杂数据类型映射到文本列时，放置在映射项的键和值之间的分隔符。默认为冒号`:`字符。|
| RECORDKEY_DELIM | 当PXF将Avro复杂数据类型映射到文本列时，放置在字段名称和记录条目之间的分隔符。 默认为冒号`:`字符。|


## <a id="avro_example"></a>示例：读取Avro数据

本节中的示例将对具有以下字段名称和数据类型模式的Avro数据进行操作:

- id - 长整型
- username - 字符串
- followers - 字符串数组
- fmap - 长整型映射
- relationship - 枚举类型
- address - 由街道号码(整型)、街道名称(字符串)、以及城市(字符串)组成的记录类型


### <a id="topic_tr3_dpg_ts__section_m2p_ztg_ts_99"></a>创建模式

执行以下操作创建一个Avro模式，以表示上述模式。

1. 创建一个名为`avro_schema.avsc`的文件:

    ``` shell
    $ vi /tmp/avro_schema.avsc
    ```

2. 将以下文本复制并粘贴到`avro_schema.avsc`中：

    ``` json
    {
    "type" : "record",
      "name" : "example_schema",
      "namespace" : "com.example",
      "fields" : [ {
        "name" : "id",
        "type" : "long",
        "doc" : "Id of the user account"
      }, {
        "name" : "username",
        "type" : "string",
        "doc" : "Name of the user account"
      }, {
        "name" : "followers",
        "type" : {"type": "array", "items": "string"},
        "doc" : "Users followers"
      }, {
        "name": "fmap",
        "type": {"type": "map", "values": "long"}
      }, {
        "name": "relationship",
        "type": {
            "type": "enum",
            "name": "relationshipEnum",
            "symbols": ["MARRIED","LOVE","FRIEND","COLLEAGUE","STRANGER","ENEMY"]
        }
      }, {
        "name": "address",
        "type": {
            "type": "record",
            "name": "addressRecord",
            "fields": [
                {"name":"number", "type":"int"},
                {"name":"street", "type":"string"},
                {"name":"city", "type":"string"}]
        }
      } ],
      "doc:" : "A basic schema for storing messages"
    }
    ```

### <a id="topic_tr3_dpgspk_15g_tsdata"></a>创建Avro数据文件(JSON)

执行以下步骤来创建符合上述模式的示例Avro文件。

1.  创建一个名为`pxf_avro.txt`的文本文件:

    ``` shell
    $ vi /tmp/pxf_avro.txt
    ```

2. 在`pxf_avro.txt`中输入以下数据：

    ``` pre
    {"id":1, "username":"john","followers":["kate", "santosh"], "relationship": "FRIEND", "fmap": {"kate":10,"santosh":4}, "address":{"number":1, "street":"renaissance drive", "city":"san jose"}}
    
    {"id":2, "username":"jim","followers":["john", "pam"], "relationship": "COLLEAGUE", "fmap": {"john":3,"pam":3}, "address":{"number":9, "street":"deer creek", "city":"palo alto"}}
    ```

    示例数据使用逗号`,`分隔顶层字段，并使用冒号`:` 分隔键-值对以及记录字段和值.

3. 将文本文件转换为Avro格式文件。有多种方法可以通过编程方式和通过命令行转换。在这个例子中，我们使用[Java Avro tools](http://avro.apache.org/releases.html)。`avro-tools-1.8.1.jar` jar文件放置在当前目录中：

    ``` shell
    $ java -jar ./avro-tools-1.8.1.jar fromjson --schema-file /tmp/avro_schema.avsc /tmp/pxf_avro.txt > /tmp/pxf_avro.avro
    ```

    生成的Avro二进制文件被写入`/tmp/pxf_avro.avro`。
    
4. 将生成的Avro文件复制到HDFS：

    ``` shell
    $ hdfs dfs -put /tmp/pxf_avro.avro /data/pxf_examples/
    ```
    
### <a id="topic_avro_querydata"></a>使用hdfs:avro配置文件查询

执行以下来创建和查询引用您在上一节添加到HDFS的`pxf_avro.avro`文件。创建表时：

-  使用PXF默认服务
-  将顶层原生字段`id` (长整型)和`username`(字符串类型)映射到其等效的Greenplum的数据库类型(bigint和text)。
-  将剩下的复杂类型映射为text类型
-  使用`hdfs:avro`配置文件的自定义选项设置记录(record)、映射(map)和集合(collection)的分隔符


1. 使用`hdfs:avro`配置文件从`pxf_avro.avro`文件创建可查询外部表：

    ``` sql
    postgres=# CREATE EXTERNAL TABLE pxf_hdfs_avro(id bigint, username text, followers text, fmap text, relationship text, address text)
                LOCATION ('pxf://data/pxf_examples/pxf_avro.avro?PROFILE=hdfs:avro&COLLECTION_DELIM=,&MAPKEY_DELIM=:&RECORDKEY_DELIM=:')
              FORMAT 'CUSTOM' (FORMATTER='pxfwritable_import');
    ```

2. 对`pxf_hdfs_avro`表执行简单查询：

    ``` sql
    postgres=# SELECT * FROM pxf_hdfs_avro;
    ```

    ``` pre
     id | username |   followers    |        fmap         | relationship |                      address                      
    ----+----------+----------------+--------------------+--------------+---------------------------------------------------
      1 | john     | [kate,santosh] | {kate:10,santosh:4} | FRIEND       | {number:1,street:renaissance drive,city:san jose}
      2 | jim      | [john,pam]     | {pam:3,john:3}      | COLLEAGUE    | {number:9,street:deer creek,city:palo alto}
    (2 rows)
    ```

    外部表的简单查询显示了复杂数据类型的组成部分，这些组成部分由`CREATE EXTERNAL TABLE`中指定的分隔符分隔。


3. 根据您的应用程序处理文本列中被分隔的部分。例如，以下命令使用Greenplum数据库内部的`string_to_array`函数将`followers`字段中的条目转换为新视图中的文本数组列。

    ``` sql
    postgres=# CREATE VIEW followers_view AS 
  SELECT username, address, string_to_array(substring(followers FROM 2 FOR (char_length(followers) - 2)), ',')::text[] 
        AS followers 
      FROM pxf_hdfs_avro;
    ```

4. 根据特定的关注者是否出现在视图中来执行查询并过滤行：

    ``` sql
    postgres=# SELECT username, address FROM followers_view WHERE followers @> '{john}';
    ```

    ``` pre
     username |                   address                   
    ----------+---------------------------------------------
     jim      | {number:9,street:deer creek,city:palo alto}
    ```

